#ifndef XG_ENTITYCREATOR_CPP
#define XG_ENTITYCREATOR_CPP
///////////////////////////////////////////////////////////////////////
#include "../EntityCreator.h"



#ifndef XG_ENTITY_EXPORT_INC_PATH
#define XG_ENTITY_EXPORT_INC_PATH	"."
#endif

#ifndef XG_ENTITY_EXPORT_SRC_PATH
#define XG_ENTITY_EXPORT_SRC_PATH	"src"
#endif

#ifndef XG_COMMENT_SPLITER
#define XG_COMMENT_SPLITER			"///////////////////////////////////////////////////////////////////////"
#endif



EntityCreator::EntityCreator(vector<ColumnData>& vColumnData, vector<string>& vPKeys, const string& name) : vec(vColumnData), pkey_vec(vPKeys), tab_name(name)
{
}
EntityCreator::~EntityCreator()
{
}
string EntityCreator::getHeaderFilePath(const string& path)
{
	string str = path;
	if (str.empty())
	{
		str = "./";
	}
	else if (str.back() != '/')
	{
		str += "/";
	}

	str += XG_ENTITY_EXPORT_INC_PATH;
	if (str.back() != '/')
	{
		str += "/";
	}
	str += getFileName();
	str += ".h";

	return str;
}
string EntityCreator::getSourceFilePath(const string& path)
{
	string str = path;
	if (str.empty())
	{
		str = "./";
	}
	else if (str.back() != '/')
	{
		str += "/";
	}

	str += XG_ENTITY_EXPORT_SRC_PATH;
	if (str.back() != '/')
	{
		str += "/";
	}

	str += getFileName();
	str += ".cpp";

	return str;
}
bool EntityCreator::create(const string& path)
{
	string hdrpath = getHeaderFilePath(path);
	string srcpath = getSourceFilePath(path);

	getClassName();

	head_name = path::name(hdrpath);

	CHECK_FALSE_RETURN(path::create(hdrpath));
	CHECK_FALSE_RETURN(path::create(srcpath));
	CHECK_FALSE_RETURN(createHeaderFile(hdrpath));
	CHECK_FALSE_RETURN(createSourceFile(srcpath));
	
	return true;
}
const ColumnData& EntityCreator::getPrimaryKeyData(const char* key_name)
{
	for (size_t i = 0; i < pkey_vec.size(); i++)
	{
		string& name = pkey_vec[i];
		if (name == key_name)
		{
			for (size_t j = 0; j < vec.size(); j++)
			{
				if (vec[j].name == key_name)
				{
					return vec[j];
				}
			}

			break;
		}
	}

	static int inited = 0;
	static ColumnData data;

	if (inited == 0)
	{
		data.name[0] = 0;
		inited = 1;
	}
	
	return data;
}
bool EntityCreator::createHeaderFile(const string& filename)
{
	string colstr;
	TextFile _out(false);

	CHECK_FALSE_RETURN(_out.open(filename, true));
	CHECK_FALSE_RETURN(writeHeader(_out));

	for (size_t i = 0; i < vec.size(); i++)
	{
		ColumnData& item = vec[i];

		if (item.type == DBData_Float || item.type == DBData_Blob)
		{
			_out<<"\t"<<item.getTypeString()<<"\t\t"<<stdx::tolower(item.name)<<";\n";
		}
		else
		{
			_out<<"\t"<<item.getTypeString()<<"\t"<<stdx::tolower(item.name)<<";\n";
		}

		if (i > 0)
		{
			colstr += string(",") + item.name;
		}
		else
		{
			colstr = item.name;
		}
	}

	_out.tr();
	_out.puts("\tstatic const char* GetTableName()");
	_out.puts("\t{");
	_out<<"\t\treturn \""<<tab_name<<"\";\n";
	_out.puts("\t}");

	_out.tr();
	_out.puts("\tstatic const char* GetColumnString()");
	_out.puts("\t{");
	_out<<"\t\treturn \""<<colstr<<"\";\n";
	_out.puts("\t}");

	_out.puts("};");
	_out.tr();
	_out.puts(XG_COMMENT_SPLITER);
	_out.puts("#endif");

	_out.flush();

	return true;
}
bool EntityCreator::createSourceFile(const string& filename)
{
	TextFile _out(false);

	CHECK_FALSE_RETURN(_out.open(filename, true));
	CHECK_FALSE_RETURN(writeSourceHeader(_out));
	CHECK_FALSE_RETURN(writeClearSource(_out));
	CHECK_FALSE_RETURN(writeInsertSource(_out));
	CHECK_FALSE_RETURN(writeNextSource(_out));
	CHECK_FALSE_RETURN(writeFindSource(_out));
	CHECK_FALSE_RETURN(writeUpdateSource(_out));
	CHECK_FALSE_RETURN(writeRemoveSource(_out));
	CHECK_FALSE_RETURN(writeGetValueSource(_out));
	CHECK_FALSE_RETURN(writeSetValueSource(_out));
	CHECK_FALSE_RETURN(writeUpdateSource(_out, true));
	
	_out.flush();

	return true;
}
bool EntityCreator::writeHeader(TextFile& _out)
{
	string str = class_name;

	stdx::toupper(str);

	_out.printf("#ifndef XG_ENTITY_%s_H\n", str.c_str());
	_out.printf("#define XG_ENTITY_%s_H\n", str.c_str());
	_out.puts(XG_COMMENT_SPLITER);
	_out.puts("#include <dbx/DBConnect.h>");
	_out.tr().tr();
	
	_out<<"class"<<" "<<class_name<<" : public DBEntity\n";
	_out.puts("{");
	_out.puts("public:");
	_out.puts("\tbool next();");
	_out.puts("\tvoid clear();");
	_out.puts("\tint insert();");
	_out.puts("\tint remove();");
	_out.puts("\tsp<QueryResult> find();");
	_out.puts("\tstring getPKCondition();");
	_out.puts("\tstring getValue(const string& key);");
	_out.puts("\tint update(bool updatenull = false);");
	_out.puts("\tbool setValue(const string& key, const string& val);");
	_out.puts("\tint remove(const string& condition, const vector<DBData*>& vec = {});");
	_out.puts("\tsp<QueryResult> find(const string& condition, const vector<DBData*>& vec = {});");
	_out.puts("\tint update(bool updatenull, const string& condition, const vector<DBData*>& vec = {});");

	_out.tr();
	_out.puts("public:");

	return true;
}
bool EntityCreator::writeSourceHeader(TextFile& _out)
{
	_out<<"#include \""<<head_name<<"\"\n";
	_out.tr().tr();

	return true;
}
bool EntityCreator::writeInsertSource(TextFile& _out)
{
	_out<<"int "<<class_name<<"::"<<"insert()\n";
	_out.puts("{");
	_out<<"\tvector<DBData*> vec;\n\n";

	_out<<"\tsql = \"INSERT INTO "<<tab_name<<"(\" + string(GetColumnString()) + \") VALUES(\";\n";

	for (size_t i = 0; i < vec.size(); i++)
	{
		ColumnData& item = vec[i];

		if (i > 0)
		{
			_out<<"\tsql += \",\";\n";
		}

		_out<<"\tsql += this->"<<stdx::tolower(item.name)<<".toValueString(conn->getSystemName());\n";

		if (vec[i].type == DBData_String || item.type == DBData_Blob)
		{
			_out<<"\tvec.push_back(&this->"<<stdx::tolower(item.name)<<");\n";
		}
	}

	_out.puts("\tsql += \")\";");
	_out.tr();
	_out<<"\treturn conn->execute(sql, vec);\n";
	_out.puts("}");

	return true;
}
bool EntityCreator::writeNextSource(TextFile& _out)
{
	int count = (int)(vec.size());

	_out<<"bool "<<class_name<<"::"<<"next()\n";
	_out.puts("{");
	_out.puts("\tif (!rs) return false;");

	_out.tr();
	_out.puts("\tsp<RowData> row = rs->next();");
	_out.tr();
	_out.puts("\tif (!row) return false;");
	_out.tr();

	for (int i = 0; i < count; i++)
	{
		ColumnData& item = vec[i];

		if (item.type == DBData_Integer)
		{
			_out<<"\tthis->"<<stdx::tolower(vec[i].name)<<" = row->getLong("<<i<<");\n";
		}
		else if (item.type == DBData_Float)
		{
			_out<<"\tthis->"<<stdx::tolower(vec[i].name)<<" = row->getDouble("<<i<<");\n";
		}
		else if (item.type == DBData_DateTime)
		{
			_out<<"\tthis->"<<stdx::tolower(vec[i].name)<<" = row->getDateTime("<<i<<");\n";
		}
		else if (item.type == DBData_Blob)
		{
			_out<<"\tthis->"<<stdx::tolower(vec[i].name)<<" = row->getBinary("<<i<<");\n";
		}
		else
		{
			_out<<"\tthis->"<<stdx::tolower(vec[i].name)<<" = row->getString("<<i<<");\n";
		}

		_out<<"\tthis->"<<stdx::tolower(vec[i].name)<<".setNullFlag(row->isNull());\n";
	}

	_out.tr();
	_out<<"\treturn true;\n";
	_out.puts("}");

	return true;
}
bool EntityCreator::writeUpdateSource(TextFile& _out, bool flag)
{
	if (flag)
	{
		_out<<"int "<<class_name<<"::"<<"update(bool updatenull, const string& condition, const vector<DBData*>& vec)\n";
	}
	else
	{
		_out<<"int "<<class_name<<"::"<<"update(bool updatenull)\n";
	}
	
	_out.puts("{");
	_out<<"\tvector<DBData*> v;\n";
	_out<<"\tsql = \"UPDATE "<<tab_name<<" SET \";\n";

	for (size_t i = 0, j = 0; i < vec.size(); i++)
	{
		ColumnData& item = vec[i];
		vector<string>::iterator it = std::find(pkey_vec.begin(), pkey_vec.end(), item.name);
		if (pkey_vec.end() == it)
		{
			_out<<"\tif (updatenull || !this->"<<stdx::tolower(item.name)<<".isNull())\n";
			_out.puts("\t{");

			_out.puts("\t\tif (sql.back() != ' ') sql += \",\";\n");

			_out<<"\t\tsql += \""<<item.name<<"=\";\n";
			_out<<"\t\tsql += this->"<<stdx::tolower(item.name)<<".toValueString(conn->getSystemName());\n";
			if (vec[i].type == DBData_String || item.type == DBData_Blob)
			{
				_out<<"\t\tv.push_back(&this->"<<stdx::tolower(item.name)<<");\n";
			}
			_out.puts("\t}");
		}
	}

	_out.tr();
	_out.puts("\tif (sql.back() == ' ') return SQLRtn_Success;\n");

	if (flag)
	{
		_out.puts("\tif (condition.empty()) return conn->execute(sql, v);\n");
		_out.puts("\tsql += \" WHERE \" + condition;\n");
		_out.puts("\tfor (auto& item : vec) v.push_back(item);\n");
	}
	else
	{
		for (size_t i = 0; i < vec.size(); i++)
		{
			const ColumnData& item = getPrimaryKeyData(vec[i].name);
			
			if (item.name[0] == 0) continue;
			
			if (item.type == DBData_String || item.type == DBData_Blob)
			{
				_out<<"\tv.push_back(&this->"<<stdx::tolower(item.name)<<");\n";
			}
		}
		
		_out.puts("\n\tsql += \" WHERE \" + getPKCondition();\n");
	}
	
	_out<<"\treturn conn->execute(sql, v);\n";
	_out.puts("}");

	return true;
}
bool EntityCreator::writeFindSource(TextFile& _out)
{
	_out<<"sp<QueryResult> "<<class_name<<"::"<<"find(const string& condition, const vector<DBData*>& vec)\n";
	_out.puts("{");
	_out<<"\tsql = \"SELECT \" + string(GetColumnString()) + \" FROM "<<tab_name<<"\";\n\n";
	_out.puts("\tif (condition.empty()) return rs = conn->query(sql);\n");
	_out.puts("\tsql += \" WHERE \";");
	_out.puts("\tsql += condition;");
	_out.tr();
	_out<<"\treturn rs = conn->query(sql, vec);\n";
	_out.puts("}");

	_out<<"sp<QueryResult> "<<class_name<<"::"<<"find()\n";
	_out.puts("{");
	_out<<"\tvector<DBData*> vec;\n";

	for (size_t i = 0; i < vec.size(); i++)
	{
		const ColumnData& item = getPrimaryKeyData(vec[i].name);
		
		if (item.name[0] == 0) continue;

		if (item.type == DBData_String || item.type == DBData_Blob)
		{
			_out<<"\tvec.push_back(&this->"<<stdx::tolower(item.name)<<");\n";
		}
	}

	_out<<"\treturn find(getPKCondition(), vec);\n";
	_out.puts("}");

	_out<<"string "<<class_name<<"::"<<"getPKCondition()\n";
	_out.puts("{");
	_out.puts("\tstring condition;");

	for (size_t i = 0, index = 0; i < vec.size(); i++)
	{
		const ColumnData& item = getPrimaryKeyData(vec[i].name);
		
		if (item.name[0] == 0) continue;

		if (0 == index++)
		{
			_out<<"\tcondition = \""<<item.name<<"=\";\n";
		}
		else
		{
			_out<<"\tcondition += \" AND \";\n";
			_out<<"\tcondition += \""<<item.name<<"=\";\n";
		}

		_out<<"\tcondition += this->"<<stdx::tolower(item.name)<<".toValueString(conn->getSystemName());\n";
	}

	_out.tr();
	_out<<"\treturn stdx::replace(condition, \"=NULL\", \"IS NULL\");\n";
	_out.puts("}");

	return true;
}
bool EntityCreator::writeRemoveSource(TextFile& _out)
{
	_out<<"int "<<class_name<<"::"<<"remove(const string& condition, const vector<DBData*>& vec)\n";
	_out.puts("{");
	_out<<"\tsql = \"DELETE FROM "<<tab_name<<"\";\n\n";
	_out.puts("\tif (condition.empty()) return conn->execute(sql);\n");
	_out.puts("\tsql += \" WHERE \";");
	_out.puts("\tsql += condition;");
	_out.tr();
	_out<<"\treturn conn->execute(sql, vec);\n";
	_out.puts("}");

	_out<<"int "<<class_name<<"::"<<"remove()\n";
	_out.puts("{");
	_out<<"\tvector<DBData*> vec;\n";
	for (size_t i = 0; i < vec.size(); i++)
	{
		const ColumnData& item = getPrimaryKeyData(vec[i].name);
		if (item.name[0] == 0)
		{
			continue;
		}

		if (item.type == DBData_String || item.type == DBData_Blob)
		{
			_out<<"\tvec.push_back(&this->"<<stdx::tolower(item.name)<<");\n";
		}
	}
	_out<<"\treturn remove(getPKCondition(), vec);\n";
	_out.puts("}");

	return true;
}
bool EntityCreator::writeClearSource(TextFile& _out)
{
	int count = (int)(vec.size());

	_out<<"void "<<class_name<<"::"<<"clear()\n";
	_out.puts("{");

	for (int i = 0; i < count; i++)
	{
		_out<<"\tthis->"<<stdx::tolower(vec[i].name)<<".clear();\n";
	}

	_out.puts("}");

	return true;
}
bool EntityCreator::writeGetValueSource(TextFile& _out)
{ 
	int count = (int)(vec.size());

	_out<<"string "<<class_name<<"::"<<"getValue(const string& key)\n";
	_out.puts("{");
	
	for (int i = 0; i < count; i++)
	{
		_out<<"\tif (key == \""<<vec[i].name<<"\") return this->"<<stdx::tolower(vec[i].name)<<".toString();\n";
	}
	
	_out.tr();
	_out.puts("\treturn stdx::EmptyString();");
	_out.puts("}");

	return true;
}
bool EntityCreator::writeSetValueSource(TextFile& _out)
{ 
	int count = (int)(vec.size());

	_out<<"bool "<<class_name<<"::"<<"setValue(const string& key, const string& val)\n";
	_out.puts("{");
	
	for (int i = 0; i < count; i++)
	{
		_out<<"\tif (key == \""<<vec[i].name<<"\")\n";
		_out.puts("\t{");
		_out<<"\t\tthis->"<<stdx::tolower(vec[i].name)<<" = val;\n";
		_out.puts("\t\treturn true;");
		_out.puts("\t}");
	}
	
	_out.tr();
	_out.puts("\treturn false;");
	_out.puts("}");

	return true;
}
int EntityCreator::GetTableColumnData(DBConnect* conn, vector<ColumnData>& vec, const string& tab_name)
{
	ColumnData item;
	string sql = GetTableQuerySQL(tab_name);
	sp<QueryResult> rs = conn->query(sql);

	if (!rs) return -1;

	int cols = rs->cols();

	if (cols <= 0)
	{
		conn->release(rs);
		
		return -1;
	}

	vec.clear();

	for (int i = 0; i < cols; i++)
	{
		if (rs->getColumnData(item, i))
		{
			string name = item.name;

			strcpy(item.name, stdx::toupper(name).c_str());
			
			vec.push_back(item);
		}
		else
		{
			conn->release(rs);
			
			return -1;
		}
	}

	conn->release(rs);
	
	return (int)(vec.size());
}
int EntityCreator::ExportEntity(DBConnect* db, const string& nmregex, const string& srcpath)
{
	string sql;
	string tabnm;
	char buffer[409600];
	bool skipped = false;
	
	size_t num = 0;
	vector<string> vec;
	vector<string> vPKeys;
	vector<ColumnData> vColumnData;

	try
	{
		regex_match(stdx::EmptyString(), regex(nmregex));
	}
	catch (...)
	{
		SetConsoleTextColor(eRED);
		puts("+++ there is a regex exception");
		SetConsoleTextColor(eWHITE);
		
		skipped = true;
	}
	
	if (db->getTables(vec) > 0)
	{
		for (size_t i = 0; i < vec.size(); i++)
		{
			stdx::toupper(tabnm = vec[i]);

			if (skipped || nmregex.empty() || regex_match(tabnm, regex(nmregex)))
			{
				++num;
			
				SetConsoleTextColor(eYELLOW);
				printf("%03d.", (int)(num));
				SetConsoleTextColor(eWHITE);
				printf("%s\n", tabnm.c_str());
				printf("+++ export entity source code...\n");
			
				if (EntityCreator::GetTableColumnData(db, vColumnData, vec[i]) > 0 && db->getPrimaryKeys(vPKeys, vec[i]) > 0)
				{
					EntityCreator creator(vColumnData, vPKeys, vec[i]);
			
					for (size_t i = 0; i < vPKeys.size(); i++) stdx::toupper(vPKeys[i]);

					if (creator.create(srcpath))
					{
						SetConsoleTextColor(eGREEN);
						puts("+++ export success");
						SetConsoleTextColor(eWHITE);
					}
					else
					{
						SetConsoleTextColor(eRED);
						puts("+++ there is a system error");
						SetConsoleTextColor(eWHITE);
			
						return -1;
					}
				}
				else
				{
					SetConsoleTextColor(eRED);
					puts("+++ database error");
					puts(db->getErrorString().c_str());
					SetConsoleTextColor(eWHITE);
			
					return -1;
				}
			}
		}
	}

	return (int)(num);
}
///////////////////////////////////////////////////////////////////////
#endif
